/*
 *  +----------------------------------------------------------------------
 *  | sophon [ A FAST GAME FRAMEWORK ]
 *  +----------------------------------------------------------------------
 *  | Copyright (c) 2023-2029 All rights reserved.
 *  +----------------------------------------------------------------------
 *  | Licensed ( http:www.apache.org/licenses/LICENSE-2.0 )
 *  +----------------------------------------------------------------------
 *  | Author: jqiris <1920624985@qq.com>
 *  +----------------------------------------------------------------------
 */

use std::{net::SocketAddr, sync::Arc};

use crate::prelude::{IServerPlugin, ServerBase};
use crate::rpcs::IServerEntity;
use crate::{configs::SslConf, error, info, logger::*};
use axum::Server;
use axum_server::tls_rustls::RustlsConfig;
use socketioxide::{adapter::Adapter, NsHandlers, SocketIoLayer};
use tower::ServiceBuilder;
use tower_http::cors::CorsLayer;

pub struct ServerSocketio<A: Adapter> {
    ssl_on: bool,
    pub inner: Arc<SocketioInner<A>>,
}

impl<A: Adapter> ServerSocketio<A> {
    pub fn new(ssl_on: bool, inner: SocketioInner<A>) -> Self {
        Self {
            ssl_on,
            inner: Arc::new(inner),
        }
    }
}

impl<A: Adapter> IServerPlugin for ServerSocketio<A> {
    fn init(&self, _s: &ServerBase) {}

    fn after_init(&self, s: &ServerBase) {
        let cfg = s.server_cfg();
        let port = cfg.client_port.clone();
        let inner = self.inner.clone();
        if self.ssl_on {
            tokio::spawn(async move {
                inner.listen_and_serve_tls(port).await;
            });
        } else {
            tokio::spawn(async move {
                inner.listen_and_serve(port).await;
            });
        }
    }

    fn before_shutdown(&self, _s: &ServerBase) {}

    fn shutdown(&self, _s: &ServerBase) {}
}

pub struct SocketioInner<A: Adapter> {
    ssl: SslConf,
    ns_handler: NsHandlers<A>,
}

impl<A: Adapter> SocketioInner<A> {
    pub fn new(ssl: SslConf, ns_handler: NsHandlers<A>) -> Self {
        Self { ssl, ns_handler }
    }
    pub async fn listen_and_serve(&self, port: i32) {
        let app = axum::Router::new().layer(
            ServiceBuilder::new()
                .layer(CorsLayer::permissive()) // Enable CORS policy
                .layer(SocketIoLayer::new(self.ns_handler.clone())),
        );
        let addr = SocketAddr::from(([0, 0, 0, 0], port as u16));
        info!("listening on {}", addr);
        if let Err(err) = Server::bind(&addr).serve(app.into_make_service()).await {
            error!("Server error: {}", err);
        }
    }
    pub async fn listen_and_serve_tls(&self, port: i32) {
        let config =
            RustlsConfig::from_pem_file(self.ssl.cert_file.clone(), self.ssl.key_file.clone())
                .await
                .unwrap();
        let app = axum::Router::new().layer(
            ServiceBuilder::new()
                .layer(CorsLayer::permissive()) // Enable CORS policy
                .layer(SocketIoLayer::new(self.ns_handler.clone())),
        );
        let addr = SocketAddr::from(([0, 0, 0, 0], port as u16));
        info!("listening on {}", addr);
        if let Err(err) = axum_server::bind_rustls(addr, config)
            .serve(app.into_make_service())
            .await
        {
            error!("Server error: {}", err);
        }
    }
}
